﻿using Engine.DynamicObjectManager;
using Engine.ObjectManager;
using Scenario_Editor.Tools;
using SharpDX;
using System;
using System.IO;
using System.Windows.Forms;

namespace Scenario_Editor.DynObjectPacer
{
    public partial class DynObjectControl : UserControl
    {
        #region Private Fields

        /// <summary>
        /// This is set, when a new object is selected. This prevents that the change-events
        /// of the controls didn´t change the properties of the selected event.
        /// </summary>
        private bool duringUpdate = false;

        private Engine.Engine engine;
        private ITerrain iTerrain;
        private SceneryObjectManager objectManager;
        private Random random;

        #endregion Private Fields

        #region Public Constructors

        public DynObjectControl()
        {
            InitializeComponent();
            this.toolTip_LockedObject.SetToolTip(this.checkBox_objectLocked, "Test _test");
        }

        #endregion Public Constructors

        #region Public Properties

        public ITerrain Terrain
        {
            get { return iTerrain; }
            set { iTerrain = value; }
        }

        #endregion Public Properties

        #region Public Methods

        public void OnInitialize(Engine.Engine engine, SceneryObjectManager objectManager)
        {
            this.engine = engine;

            OnSceneryObjectManagerChange(objectManager, null);

            this.fileTreeView.SearchPattern = "*.MOX|*.TREE";
            this.fileTreeView.SetStartFolder(this.objectManager.objectFolder);

            this.random = new Random(DateTime.Now.Millisecond);
        }

        public void OnSceneryObjectManagerChange(object sender, EventArgs e)
        {
            this.objectManager = (SceneryObjectManager)sender;

            if (this.objectManager != null)
            {
                // Register events for object manager. they fired when some properties changed.
                this.objectManager.DeleteNode += new Engine.ObjectManager.DynObjectManager.DeleteNodeHandler(objectManager_DeleteNode);
                this.objectManager.AddNode += new DynObjectManager.AddNodeHandler(objectManager_AddNode);
                this.objectManager.NodePropertiesChanged += new DynObjectManager.NodePropertiesHandler(objectManager_NodePropertiesChanged);
                this.objectManager.SelectionChanged += new DynObjectManager.SelectionChangedHandler(objectManager_SelectionChanged);
                this.objectManager.ActionMode = Engine.ObjectManager.DynObjectManager.ActionModeEnum.MoveNode;
            }
        }

        public void OnTerrainChange(object sender, EventArgs e)
        {
            this.iTerrain = (ITerrain)sender;
        }

        /// <summary>
        /// Sets all terrain to terrain ground.
        /// </summary>
        public void SetObjectsOnGround()
        {
            for (int i = 0; i < this.objectManager.Nodes.Count; i++)
            {
                if (this.objectManager.Nodes[i].SetToTerrain)
                {
                    BaseNode baseNode = this.objectManager.Nodes[i];
                    Vector3 position = this.objectManager.Nodes[i].Position;
                    position.Y = iTerrain.GetHeightFromPosition(this.objectManager.Nodes[i].Position);
                    this.objectManager.Nodes[i].Position = position;

                    float angle, scale;
                    this.objectManager.GetProperties(baseNode, out position, out angle, out scale);
                    this.objectManager.DeleteObject((string)baseNode.Properties, baseNode);
                    this.objectManager.AddObject(baseNode, angle, scale);
                    this.objectManager.UpdateBoundingBox(baseNode, (float)this.numericUpDown_rotation.Value, scale);
                }
            }
        }

        public void UseThisTool(Vector3 positionIn3dSpace, bool isPositionFromTerrain, bool mouseInRenderwindow, NumericUpDown zHeight, Panel renderWindow)
        {
            if (this.objectManager == null)
                return;

            bool allowedToSetNewNodes = File.Exists(this.fileTreeView.SelectedFullPath);
            switch (this.objectManager.ActionMode)
            {
                case DynObjectManager.ActionModeEnum.NewNode:
                    if (this.engine.Mouse.ButtonRightPressingStarted && !this.engine.Mouse.HasMovedWhilePressingR)
                    {
                        this.numericUpDown_rotation.Value = this.random.Next(0, 359);
                        this.numericUpDown_scale.Value = (decimal)this.random.Next(7, 15) * 0.1m;
                    }
                    break;
            }
            try
            {
                this.objectManager.UseThisTool(positionIn3dSpace, isPositionFromTerrain, mouseInRenderwindow, allowedToSetNewNodes, zHeight, renderWindow);
                if (this.objectManager.ActionMode == DynObjectManager.ActionModeEnum.MoveNode && (this.objectManager.SelectedIndex != -1 && !this.objectManager.Nodes[this.objectManager.SelectedIndex].Locked) || this.objectManager.SelectedIndex == -1)
                {
                    this.toolTip_LockedObject.Hide(this.checkBox_objectLocked);
                }
            }
            catch (LockedException ex)
            {
                this.toolTip_LockedObject.Show("Object locked! To move the object you have to uncheck the checkbox.", this.checkBox_objectLocked);
            }
        }

        #endregion Public Methods

        #region Internal Methods

        internal void CycleDrawMode()
        {
            this.objectManager.CycleDrawMode();
        }

        #endregion Internal Methods

        #region Private Methods

        private void buttonDeleteStructure_CheckedChanged(object sender, EventArgs e)
        {
            CheckBox button = (CheckBox)sender;
            if (button.Checked)
                this.objectManager.ActionMode = Engine.ObjectManager.DynObjectManager.ActionModeEnum.DeleteNode;
            if (this.buttonDeleteStructure.Checked)
                this.buttonMoveStructure.Checked = false;
        }

        private void buttonMoveStructure_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton button = (RadioButton)sender;
            if (button.Checked)
                this.objectManager.ActionMode = Engine.ObjectManager.DynObjectManager.ActionModeEnum.MoveNode;
        }

        private void buttonNewStructure_CheckedChanged(object sender, EventArgs e)
        {
            RadioButton button = (RadioButton)sender;
            if (button.Checked)
                this.objectManager.ActionMode = Engine.ObjectManager.DynObjectManager.ActionModeEnum.NewNode;
        }

        private void fileTreeView_Click_1()
        {
            if (!this.duringUpdate)
            {
                if (File.Exists(this.fileTreeView.SelectedFullPath))
                    this.objectManager.ChangeSelectedNode();
            }
        }

        private void objectManager_AddNode(BaseNode baseNode, EventArgs e)
        {
            baseNode.Properties = this.fileTreeView.SelectedPath;
            baseNode.SetToTerrain = this.checkBox_SetOnTerrain.Checked;
            baseNode.HeightOverTerrain = (float)this.numericUpDown_OffsetHeight.Value;

            float angle = (float)((double)this.numericUpDown_rotation.Value * Math.PI / 180.0);
            float scale = (float)this.numericUpDown_scale.Value;
            this.objectManager.AddObject(baseNode, angle, scale);
            this.objectManager.UpdateBoundingBox(baseNode, (float)this.numericUpDown_rotation.Value, scale);
        }

        private void objectManager_DeleteNode(BaseNode baseNode, EventArgs e)
        {
            this.objectManager.DeleteObject((string)baseNode.Properties, baseNode);
            this.buttonDeleteStructure.Checked = false;
            this.buttonMoveStructure.Checked = true;
        }

        private void objectManager_NodePropertiesChanged(BaseNode baseNode, EventArgs e)
        {
            if (baseNode.Properties != null)
            {
                this.duringUpdate = true;
                this.objectManager.DeleteObject((string)baseNode.Properties, baseNode);

                // set base node properties.
                baseNode.Properties = this.fileTreeView.SelectedPath;
                baseNode.Locked = this.checkBox_objectLocked.Checked;
                this.numericUpDown_positionX.Value = (decimal)baseNode.Position.X;
                this.numericUpDown_positionY.Value = (decimal)baseNode.Position.Y;
                this.numericUpDown_positionZ.Value = (decimal)baseNode.Position.Z;
                //Vector3 position = new Vector3();
                //position.X = (float)this.numericUpDown_positionX.Value;
                //position.Y = (float)this.numericUpDown_positionY.Value;
                //position.Z = (float)this.numericUpDown_positionZ.Value;
                //baseNode.Position = position;

                // TODO: Update the bounding box.

                float angle = (float)((double)this.numericUpDown_rotation.Value * Math.PI / 180.0);
                float scale = (float)this.numericUpDown_scale.Value;
                this.objectManager.AddObject(baseNode, angle, scale);
                this.objectManager.UpdateBoundingBox(baseNode, (float)this.numericUpDown_rotation.Value, scale);

                this.duringUpdate = false;
            }
        }

        private void objectManager_SelectionChanged(BaseNode baseNode, EventArgs e)
        {
            this.duringUpdate = true;
            Vector3 position;
            float angle, scale;
            this.fileTreeView.SetNode((string)baseNode.Properties);
            this.objectManager.GetProperties(baseNode, out position, out angle, out scale);

            decimal newAngle = (decimal)Math.Round((angle / Math.PI * 180.0), 1);

            while (newAngle < 0.0m) newAngle += 360.0m;
            while (newAngle >= 360.0m) newAngle = 360.0m;

            this.numericUpDown_rotation.Value = newAngle;
            this.numericUpDown_scale.Value = (decimal)scale;
            this.numericUpDown_positionX.Value = (decimal)baseNode.Position.X;
            this.numericUpDown_positionY.Value = (decimal)baseNode.Position.Y;
            this.numericUpDown_positionZ.Value = (decimal)baseNode.Position.Z;
            this.checkBox_objectLocked.Checked = baseNode.Locked;
            this.checkBox_SetOnTerrain.Checked = baseNode.SetToTerrain;

            this.numericUpDown_OffsetHeight.Value = (decimal)baseNode.HeightOverTerrain;

            this.duringUpdate = false;
        }

        private void OnControlPropertiesChange(object sender, EventArgs e)
        {
            if (this.numericUpDown_rotation.Value >= 360.0m)
                this.numericUpDown_rotation.Value -= 360.0m;
            if (this.numericUpDown_rotation.Value < 0.0m)
                this.numericUpDown_rotation.Value += 360.0m;

            if (!duringUpdate)
            {
                if (this.objectManager.SelectedIndex > -1)
                {
                    BaseNode baseNode = this.objectManager.Nodes[this.objectManager.SelectedIndex];
                    Vector3 position = new Vector3();
                    position.X = (float)this.numericUpDown_positionX.Value;
                    position.Y = (float)this.numericUpDown_positionY.Value;
                    position.Z = (float)this.numericUpDown_positionZ.Value;
                    baseNode.Position = position;

                    baseNode.Locked = this.checkBox_objectLocked.Checked;
                    baseNode.SetToTerrain = this.checkBox_SetOnTerrain.Checked;
                    baseNode.HeightOverTerrain = (float)this.numericUpDown_OffsetHeight.Value;
                }
                if (File.Exists(this.fileTreeView.SelectedFullPath))
                    this.objectManager.ChangeSelectedNode();
            }
        }

        private void SelectTextOnEnter(object sender, EventArgs e)
        {
            if (sender.GetType() == typeof(NumericUpDown))
            {
                NumericUpDown numericUpDown = sender as NumericUpDown;
                numericUpDown.Select(0, numericUpDown.ToString().Length);
            }
        }

        #endregion Private Methods
    }
}